#pragma once

#include <memory>
#include <stdint.h>
#include <unistd.h>

namespace ibox::network {

/**
 * 缓冲区类
 *
 * m_buffer_ptr                        m_buffer_size
 *   |                                      |
 *   v                                      v
 *   +----+----------------+----------------+
 *   |    | readable bytes | writable bytes |
 *   +----+----------------+----------------+
 *        ^                ^
 *        |                |
 *    m_read_index       m_write_index
 *
 * 使用示例：
 *  Buffer b;
 *  b.append("abc", 4);  // 往缓冲中写入4字节的数据
 *  char buff[10];
 *  b.fetch(buff, 4);    // 从缓冲中取出4字节的数据
 *
 *  b.ensure_writable_size(10);   // 预留10个字节
 *  memset(b.writable_begin(), 0xcc, 10);    // 将该10个字节全置为0xcc
 *  b.has_written(10);           // 标该已写入10个字节
 *
 * \warnning    多线程使用需在外部加锁
 */
class Buffer {
public:
    static const size_t initialSize = 256;

    explicit Buffer(size_t reverse_size = initialSize);
    ~Buffer() = default;

    Buffer(const Buffer &other);
    Buffer(Buffer &&other) noexcept;

    Buffer &operator=(const Buffer &other);
    Buffer &operator=(Buffer &&other) noexcept;

    void swap(Buffer &other);
    void reset();

public:
    /**
     * 写缓冲操作
     */

    // 获取可写空间大小
    inline size_t writable_size() const {
        return m_buffer_size - m_write_index;
    }

    // 保障有指定容量的可写空间
    bool ensure_writable_size(size_t write_size);

    // 获取写首地址
    inline uint8_t *writable_begin() const {
        return (m_buffer_ptr != nullptr) ? (m_buffer_ptr.get() + m_write_index)
                                         : nullptr;
    }

    // 标记已写入数据大小
    void has_written(size_t write_size);

    // 往缓冲区追加指定的数据块，返回实现写入的大小
    size_t append(const void *p_data, size_t data_size);

    /**
     * 读缓冲操作
     */

    // 获取可读区域大小
    inline size_t readable_size() const { return m_write_index - m_read_index; }

    // 获取可读区首地址
    inline uint8_t *readable_begin() const {
        return (m_buffer_ptr != nullptr) ? (m_buffer_ptr.get() + m_read_index)
                                         : nullptr;
    }

    // 标记已读数据大小
    void has_read(size_t read_size);

    // 标记已读取全部数据
    void has_readall();

    // 从缓冲区读取指定的数据块，返回实际读到的数据大小
    size_t fetch(void *p_buff, size_t buff_size);

    /**
     * 其它
     */
    // 缩减缓冲多余容量
    void shrink();

protected:
    void clone_from(const Buffer &other);

private:
    std::unique_ptr<uint8_t[]> m_buffer_ptr{}; // 缓冲区地址
    size_t m_buffer_size{};                    // 缓冲区大小

    size_t m_read_index{};  // 读位置偏移
    size_t m_write_index{}; // 写位置偏移
};

} // namespace ibox::network
